package com.hzncc.flowable_diboot.project_contracts.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.diboot.core.binding.Binder;
import com.diboot.core.binding.QueryBuilder;
import com.diboot.core.entity.BaseModel;
import com.diboot.core.holder.ThreadLocalHolder;
import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.S;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import com.diboot.iam.entity.IamUser;
import com.diboot.iam.mapper.IamUserMapper;
import com.diboot.iam.util.IamSecurityUtils;
import com.diboot.tenant.entity.IamTenant;
import com.diboot.tenant.mapper.IamTenantMapper;
import com.diboot.tenant.service.IamTenantService;
import com.hzncc.flowable_diboot.contract.entity.ContractAudit;
import com.hzncc.flowable_diboot.contract.entity.SalesContract;
import com.hzncc.flowable_diboot.contract.service.ContractTeamService;
import com.hzncc.flowable_diboot.contract.service.SalesContractService;
import com.hzncc.flowable_diboot.contract.service.impl.OaInterfaceService;
import com.hzncc.flowable_diboot.entity.SapCustomer;
import com.hzncc.flowable_diboot.entity.contract.Contract;
import com.hzncc.flowable_diboot.enums.*;
import com.hzncc.flowable_diboot.excel.service.MultiSheetExcelImportService;
import com.hzncc.flowable_diboot.exception.BusinessException;
import com.hzncc.flowable_diboot.mapper.ContractAuditMapper;
import com.hzncc.flowable_diboot.mapper.ContractMapper;
import com.hzncc.flowable_diboot.mapper.SalesContractMapper;
import com.hzncc.flowable_diboot.mapper.SapCustomerMapper;
import com.hzncc.flowable_diboot.project_contracts.dto.ProjectContractAuditDTO;
import com.hzncc.flowable_diboot.project_contracts.entity.*;
import com.hzncc.flowable_diboot.project_contracts.excel.listener.HistoricalFinancialDataImportListener;
import com.hzncc.flowable_diboot.project_contracts.excel.model.HistoricalFinancialDataImportModel;
import com.hzncc.flowable_diboot.project_contracts.mapper.ProjectContractAuditMapper;
import com.hzncc.flowable_diboot.project_contracts.service.*;
import com.hzncc.flowable_diboot.project_contracts.vo.*;
import com.hzncc.flowable_diboot.service.impl.CommonService;
import com.hzncc.flowable_diboot.util.WordUtils;
import com.sap.conn.jco.JCoException;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;


/**
 * 项目类合同评审 相关Service实现类
 *
 * @author MyName
 * @version 1.0
 * @date 2024-09-24
 * Copyright © MyCorp
 */
@Log4j2
@Service
public class ProjectContractAuditServiceImpl extends BaseServiceImpl<ProjectContractAuditMapper, ProjectContractAudit> implements ProjectContractAuditService {

    @Autowired
    private ProjectContractAmountService projectContractAmountService;

    @Autowired
    private ProjectContractItemService projectContractItemService;

    @Autowired
    private ProjectSamContractService projectSamContractService;

    @Autowired
    private ProjectSamContractEquipmentService projectSamContractEquipmentService;

    @Autowired
    private ProjectContractUpdateService projectContractUpdateService;

    @Autowired
    private ProjectContractCancelService projectContractCancelService;

    @Autowired
    private CommonService commonService;

    @Autowired
    private ProjectContractAuditMapper projectContractAuditMapper;

    @Autowired
    private OaInterfaceService oaInterfaceService;

    @Autowired
    private ProjectPaymentCollectionPlanService projectPaymentCollectionPlanService;

    @Autowired
    private ContractAuditMapper contractAuditMapper;

    @Autowired
    private SalesContractMapper salesContractMapper;

    @Autowired
    private IamTenantMapper iamTenantMapper;

    @Autowired
    private SalesContractService salesContractService;

    @Autowired
    private MultiSheetExcelImportService multiSheetExcelImportService;

    @Autowired
    private SapCustomerMapper sapCustomerMapper;

    @Autowired
    private IamUserMapper iamUserMapper;

    @Autowired
    private ContractMapper contractMapper;

    @Autowired
    private ContractTeamService contractTeamService;

    @Autowired
    private IamTenantService iamTenantService;

    @Override
    protected void afterCreate(ProjectContractAudit entity) {
        createOrUpdateN2NRelations(ProjectContractAuditUser::getProjectContractAudit, entity.getId(), ProjectContractAuditUser::getUser, entity.getStarUids());
    }

    @Override
    protected void afterUpdate(ProjectContractAudit entity) {
        createOrUpdateN2NRelations(ProjectContractAuditUser::getProjectContractAudit, entity.getId(), ProjectContractAuditUser::getUser, entity.getStarUids());
    }

    /**
     * 保存项目类合同评审
     *
     * @param projectContractAudit
     * @return
     */
    @Override
    @Transactional
    public String saveProjectContractAudit(ProjectContractAuditDetailVO projectContractAudit) {
        if (S.isBlank(projectContractAudit.getId())) {
            projectContractAudit.setId(null);
        }
        projectContractAudit.setAuditTime(LocalDateTime.now());
        // 拆分条件判断
        final String contractStatus = projectContractAudit.getContractStatus();
        final boolean isEditableStatus = !Objects.equals(contractStatus, ContractAuditStatusEnum.HISTORY.getCode());

        final String contractCode = projectContractAudit.getContractCode();
        final String id = projectContractAudit.getId();
        final String auditType = projectContractAudit.getAuditType();
        // 独立校验逻辑
        if (isEditableStatus && validateCode(contractCode, id, auditType)) {
            throw new BusinessException(String.format("合同编号重复[编号：%s]",
                    contractCode));
        }
        // 根据项目合同审核ID的空白状态，确定项目和销售合同变更标志
        if (S.isBlank(projectContractAudit.getId())) {
            // 如果ID为空，表明是新的审核记录，设置项目和销售合同变更标志为1
            projectContractAudit.setProjChangeFlag(1);
            projectContractAudit.setSamChangeFlag(1);
        } else {
            // 如果ID非空，表明是 existing 的审核记录，需要对比详细信息是否发生变化
            ProjectContractAuditDetailVO detail = this.getViewObject(projectContractAudit.getId(), ProjectContractAuditDetailVO.class);
            // 检查项目合同信息是否发生变化
            if (!detail.equals(projectContractAudit) || !Objects.equals(detail.collectItemList(), projectContractAudit.collectItemList()) || !Objects.equals(detail.getProjectContractUpdate(), projectContractAudit.getProjectContractUpdate())) {
                projectContractAudit.setProjChangeFlag(1);
            }
            // 检查销售合同信息是否发生变化
            if (Objects.equals(detail.getProjectSamContract(), projectContractAudit.getProjectSamContract())) {
                projectContractAudit.setSamChangeFlag(1);
            }
        }
        projectContractAudit.setContractVersion(Optional.ofNullable(projectContractAudit.getContractVersion()).orElse(1L));
        // 创建或更新项目合同审核实体
        this.createOrUpdateEntity(projectContractAudit);

        // 获取并更新项目合同金额详情
        ProjectContractAmountDetailVO projectContractAmount = projectContractAudit.getProjectContractAmount();
        projectContractAmount.setContractId(projectContractAudit.getId());
        projectContractAmountService.createOrUpdateEntity(projectContractAmount);

        // 获取各类合同项目列表
        List<ProjectContractItemDetailVO> contractItemList = projectContractAudit.getContractItemList();
        List<ProjectContractItemDetailVO> homemadeItemList = projectContractAudit.getHomemadeItemList();
        List<ProjectContractItemDetailVO> salesItemList = projectContractAudit.getSalesItemList();
        List<ProjectContractItemDetailVO> noCostItemList = projectContractAudit.getNoCostItemList();

        // 合并所有项目列表，以便统一处理
        List<ProjectContractItemDetailVO> itemList = new ArrayList<>();
        if (contractItemList != null) {
            // 合并合同项目列表
            for (ProjectContractItemDetailVO itemDetailVO : contractItemList) {
                itemDetailVO.setItemType(ItemtypeEnum.project.getCode());
                itemDetailVO.setContractId(projectContractAudit.getId());
                itemList.add(itemDetailVO);
            }
        }
        if (homemadeItemList != null) {
            // 合并自制项目列表
            for (ProjectContractItemDetailVO itemDetailVO : homemadeItemList) {
                itemDetailVO.setItemType(ItemtypeEnum.homemade.getCode());
                itemDetailVO.setContractId(projectContractAudit.getId());
                itemList.add(itemDetailVO);
            }
        }
        if (salesItemList != null) {
            // 合并销售项目列表
            for (ProjectContractItemDetailVO itemDetailVO : salesItemList) {
                itemDetailVO.setItemType(ItemtypeEnum.sales.getCode());
                itemDetailVO.setContractId(projectContractAudit.getId());
                itemList.add(itemDetailVO);
            }
        }
        if (noCostItemList != null) {
            // 合并无成本项目列表
            for (ProjectContractItemDetailVO itemDetailVO : noCostItemList) {
                itemDetailVO.setItemType(ItemtypeEnum.noCost.getCode());
                itemDetailVO.setContractId(projectContractAudit.getId());
                itemList.add(itemDetailVO);
            }
        }
        // 创建或更新合并后的项目列表
        projectContractItemService.createOrUpdateEntities(itemList);
        // 收款计划
        List<ProjectPaymentCollectionPlanDetailVO> planList = projectContractAudit.getPlanList();
        if (planList != null) {
            planList.forEach(p -> p.setContractId(projectContractAudit.getId()));
        }
        projectPaymentCollectionPlanService.createOrUpdateEntities(planList);
        // 产品供销合同
        ProjectSamContractDetailVO projectSamContract = projectContractAudit.getProjectSamContract();
        if (projectSamContract != null) {
            projectSamContract.setContractId(projectContractAudit.getId());
            projectSamContractService.createOrUpdateEntity(projectSamContract);
            // 产品供销设备
            List<ProjectSamContractEquipmentDetailVO> equipmentDetailVOS = projectSamContract.getEquipmentDetailVOS();
            if (equipmentDetailVOS != null) {
                equipmentDetailVOS.forEach(e -> e.setSamContractId(projectSamContract.getId()));
                projectSamContractEquipmentService.createOrUpdateEntities(equipmentDetailVOS);
            }
        }
        // 保存合同变更
        ProjectContractUpdateDetailVO projectContractUpdate = projectContractAudit.getProjectContractUpdate();
        if (projectContractUpdate != null) {
            projectContractUpdate.setContractId(projectContractAudit.getId());
            projectContractUpdateService.createOrUpdateEntity(projectContractUpdate);
        }
        // 保存合同取消
        ProjectContractCancelDetailVO projectContractCancel = projectContractAudit.getProjectContractCancel();
        if (projectContractCancel != null) {
            projectContractCancel.setContractId(projectContractAudit.getId());
            projectContractCancelService.createOrUpdateEntity(projectContractCancel);
        }
        return projectContractAudit.getId();
    }

    /**
     * 提交项目类合同评审
     *
     * @param projectContractAudit
     */
    @Override
    @Transactional
    public String submitProjectContractAudit(ProjectContractAuditDetailVO projectContractAudit) {

        return this.saveProjectContractAudit(projectContractAudit);
    }

    /**
     * 校验合同编号是否重复
     *
     * @param code 合同编号
     * @return true 有重复 false 无重复
     */
    @Override
    public Boolean validateCode(String code, String id, String type) {
        LambdaQueryWrapper<ContractAudit> contractAuditLambdaQueryWrapper = new LambdaQueryWrapper<>();
        contractAuditLambdaQueryWrapper.eq(ContractAudit::getContractCode, code).eq(ContractAudit::isDeleted, 0);
        Long sysCount = contractAuditMapper.selectCount(contractAuditLambdaQueryWrapper);
        QueryWrapper<ProjectContractAudit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contract_code", code);
        ProjectContractAudit entity = getEntity(id);
        if (S.equals(type, "create")) {
            return !(entity != null && S.equals(code, entity.getContractCode())) && (sysCount > 0 || projectContractAuditMapper.selectCount(queryWrapper) > 0);
        } else return !S.equals(type, "update") && !S.equals(type, "cancel");
    }

    /**
     * 推送OA
     *
     * @param projectContractAudit
     */
    @Transactional(rollbackFor = Exception.class)
    public void pushOa(ProjectContractAuditDetailVO projectContractAudit, String type) {
        MultiValueMap<String, Object> param = null;
        if (projectContractAudit.getSalesItemList() != null) {
            projectContractAudit.getSalesItemList().forEach(e -> {
                e.setInquirerAndPhone(toStr(e.getInquirer(), "") + " " + toStr(e.getInquirerPhoneNumber(), ""));
            });
        }
        if (projectContractAudit.getContractItemList() != null) {
            projectContractAudit.getContractItemList().forEach(e -> {
                e.setInquirerAndPhone(toStr(e.getInquirer(), "") + " " + toStr(e.getInquirerPhoneNumber(), ""));
            });
        }
        if (projectContractAudit.getHomemadeItemList() != null) {
            projectContractAudit.getHomemadeItemList().forEach(e -> {
                e.setInquirerAndPhone(toStr(e.getInquirer(), "") + " " + toStr(e.getInquirerPhoneNumber(), ""));
            });
        }
        if (projectContractAudit.getNoCostItemList() != null) {
            projectContractAudit.getNoCostItemList().forEach(e -> {
                e.setInquirerAndPhone(toStr(e.getInquirer(), "") + " " + toStr(e.getInquirerPhoneNumber(), ""));
            });
        }
        try {
            String templateId = null;
            String code = null;
            if (S.equals(type, "sam")) { // 供销合同
                if (projectContractAudit.getSamChangeFlag() != null && projectContractAudit.getSamChangeFlag() != 1) {
                    throw new RuntimeException("所有变更已推送");
                }
                if ((S.equals(projectContractAudit.getSamAuditType(), "cancel") || S.equals(projectContractAudit.getSamAuditType(), "update")) && !S.equals(projectContractAudit.getSamOaStatus(), OAStatusEnum.RESP.getCode())) {
                    throw new RuntimeException("有未完成的OA评审，暂时无法推送OA");
                } else if (!S.equals(projectContractAudit.getSamOaStatus(), OAStatusEnum.NO.getCode()) && !S.equals(projectContractAudit.getSamOaStatus(), OAStatusEnum.FAIL.getCode())) {
                    throw new RuntimeException("合同已推送,请勿重复推送");
                }
                templateId = oaInterfaceService.getOaTemplateId("sam-" + projectContractAudit.getBusinessModel(), projectContractAudit.getAuditType());
                code = "sam-" + projectContractAudit.getBusinessModel() + "-" + projectContractAudit.getSamAuditType();
                projectContractAudit.setAuditType("sam-" + projectContractAudit.getAuditType());
            } else if (S.equals(type, "proj")) { // 项目合同
                if (projectContractAudit.getProjChangeFlag() != null && projectContractAudit.getProjChangeFlag() != 1) {
                    throw new RuntimeException("所有变更已推送");
                }
//                if (!S.equals(projectContractAudit.getOaStatus(), OAStatusEnum.NO.getCode()) && !S.equals(projectContractAudit.getOaStatus(), OAStatusEnum.FAIL.getCode()) && !S.equals(projectContractAudit.getContractStatus(), ContractAuditStatusEnum.OA_FAIL.getCode())) {
//                    throw new RuntimeException("合同已推送,请勿重复推送");
//                }
                templateId = oaInterfaceService.getOaTemplateId("proj-" + projectContractAudit.getBusinessModel(), projectContractAudit.getAuditType());
                code = "proj-" + projectContractAudit.getBusinessModel() + "-" + projectContractAudit.getAuditType();
                projectContractAudit.setAuditType("proj-" + projectContractAudit.getAuditType());
            } else if (S.equals(type, "all")) {
                pushOa(projectContractAudit, "proj");
                pushOa(projectContractAudit, "sam");
                return;
            }
            if (S.contains(projectContractAudit.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode())) { // 合同变更
                Map<String, List<ProjectContractItem>> compare = compareItemChange(projectContractAudit.getId());
                projectContractAudit.setAddList(compare.get("add") == null ? new ArrayList<>() : compare.get("add"));
                projectContractAudit.setDelList(compare.get("del") == null ? new ArrayList<>() : compare.get("del"));
            }
            if (S.contains(projectContractAudit.getAuditType(), ContractAuditTypeEnum.CANCEL.getCode())) { // 合同取消
                List<ProjectContractItemDetailVO> contractItemList = projectContractAudit.getContractItemList();
                List<Map<String, String>> list = new ArrayList<>();
                Map<String, String> map = new HashMap<>();
                map.put("desc", contractItemList.stream().map(ProjectContractItem::getEquipmentName).collect(Collectors.joining("/")));
                map.put("sapCode", projectContractAudit.getSapContractCode());
                list.add(map);
                projectContractAudit.getProjectContractCancel().setSapList(list);
            }

            param = oaInterfaceService.buildOaParam(projectContractAudit, templateId, code);
        } catch (Exception e) {
            log.error("项目合同OA参数构建失败", e);
            throw new RuntimeException(e);
        }
        String url = "/api/km-review/kmReviewRestService/addReview";
        String msg;
        String res = OAStatusEnum.SUCCESS.getCode();
        String contractStatus = ContractAuditStatusEnum.OA_PROCESSING.getCode();
        try {

            msg = oaInterfaceService.accessOaInterface(url, param);
            if (S.contains(msg, "user is not exist") || S.contains(msg, "null") || msg == null) {
                res = OAStatusEnum.FAIL.getCode();
                if (projectContractAudit.getAuditType() != null && S.contains(projectContractAudit.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode())) {
                    contractStatus = ContractAuditStatusEnum.NO_SUBMIT.getCode();
                } else {
                    // 回滚合同状态以便于再次推送
                    contractStatus = ContractAuditStatusEnum.OA_SUCCESS.getCode();
                }
                if (projectContractAudit.getAuditType() != null && S.contains(projectContractAudit.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode())) {
                    UpdateWrapper<ProjectContractAudit> uw = new UpdateWrapper<>();
                    uw.lambda()
                            .set(ProjectContractAudit::getOaMsg, msg)
                            .set(ProjectContractAudit::getOaStatus, res)
                            .set(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.OA_SUCCESS.getCode())
                            .eq(ProjectContractAudit::getContractCode, projectContractAudit.getContractCode())
                            .eq(ProjectContractAudit::getContractVersion, projectContractAudit.getContractVersion() - 1);
                    projectContractAuditMapper.update(uw);
                }
                msg = "推送失败，" + msg;
            } else {
                UpdateWrapper<SalesContract> updateWrapper = new UpdateWrapper<>();
                updateWrapper.lambda()
                        .set(SalesContract::getProjectContractId, projectContractAudit.getId())
                        .eq(SalesContract::getOrderCode, projectContractAudit.getContractCode());
                salesContractMapper.update(updateWrapper);
            }
        } catch (Exception e) {
            msg = "推送失败，系统错误:" + e.getMessage();
            res = OAStatusEnum.FAIL.getCode();
            if (projectContractAudit.getAuditType() != null && S.contains(projectContractAudit.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode())) {
                contractStatus = ContractAuditStatusEnum.NO_SUBMIT.getCode();
            } else {
                contractStatus = ContractAuditStatusEnum.OA_SUCCESS.getCode();
            }
            if (projectContractAudit.getAuditType() != null && S.contains(projectContractAudit.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode())) {
                UpdateWrapper<ProjectContractAudit> uw = new UpdateWrapper<>();
                uw.lambda()
                        .set(ProjectContractAudit::getOaMsg, msg)
                        .set(ProjectContractAudit::getOaStatus, res)
                        .set(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.OA_SUCCESS.getCode())
                        .eq(ProjectContractAudit::getContractCode, projectContractAudit.getContractCode())
                        .eq(ProjectContractAudit::getContractVersion, projectContractAudit.getContractVersion() - 1);
                projectContractAuditMapper.update(uw);
            }
            log.error("OA推送失败", e);
        }
        UpdateWrapper<ProjectContractAudit> updateWrapper = new UpdateWrapper<>();
        if (S.equals(type, "sam")) {
            updateWrapper.lambda()
                    .set(ProjectContractAudit::getSamOaMsg, msg)
                    .set(ProjectContractAudit::getSamOaStatus, res)
                    .set(ProjectContractAudit::getContractStatus, contractStatus)
                    .eq(ProjectContractAudit::getId, projectContractAudit.getId());
        } else {
            updateWrapper.lambda()
                    .set(ProjectContractAudit::getOaMsg, msg)
                    .set(ProjectContractAudit::getOaStatus, res)
                    .set(ProjectContractAudit::getContractStatus, contractStatus)
                    .eq(ProjectContractAudit::getId, projectContractAudit.getId());
        }
        this.updateEntity(updateWrapper);
    }

    private String toStr(Object o, String defaultValue) {
        return Optional.ofNullable(o).orElse(defaultValue).toString();
    }

    /**
     * 复制项目类合同评审
     *
     * @param id 合同ID
     * @return 复制后的合同评审详细信息
     */
    @Override
    public ProjectContractAuditDetailVO copyProjectContractAudit(String id) {
        // 根据ID获取合同评审详细信息
        ProjectContractAuditDetailVO contractAuditDetailVO = this.getViewObject(id, ProjectContractAuditDetailVO.class);
        if (contractAuditDetailVO == null) {
            // 如果合同不存在，则抛出业务异常
            throw new BusinessException("合同不存在");
        }
        // 清除原有ID以支持复制操作
        contractAuditDetailVO.setId(null);
        // 清除合同编码以确保复制的合同能被正确识别
        contractAuditDetailVO.setContractCode(null);
        contractAuditDetailVO.setExternalContractCode(null);
        contractAuditDetailVO.setContractStatus(ContractAuditStatusEnum.NO_SUBMIT.getCode());
        contractAuditDetailVO.setOaStatus(OAStatusEnum.NO.getCode());
        contractAuditDetailVO.setOaMsg(null);
        contractAuditDetailVO.setAuditType(ContractAuditTypeEnum.CREATE.getCode());
        contractAuditDetailVO.setSapContractCode(null);
        contractAuditDetailVO.setSapMsg(null);
        contractAuditDetailVO.setSapStatus(null);
        clearBaseModel(contractAuditDetailVO);
        // 获取并处理合同更新详情
        contractAuditDetailVO.setProjectContractUpdate(null);
        // 获取并处理合同取消详情
        contractAuditDetailVO.setProjectContractCancel(null);
        // 获取并处理项目合同详情
        ProjectSamContractDetailVO projectSamContract = contractAuditDetailVO.getProjectSamContract();
        if (projectSamContract != null) {
            // 清除关联ID以支持复制操作
            projectSamContract.setId(null);
            projectSamContract.setContractId(null);
            clearBaseModel(projectSamContract);
            // 处理合同设备详情
            List<ProjectSamContractEquipmentDetailVO> equipmentDetailVOS = projectSamContract.getEquipmentDetailVOS();
            if (equipmentDetailVOS != null) {
                // 清除设备详情中的关联ID以支持复制操作
                equipmentDetailVOS.forEach(e -> {
                    e.setSamContractId(null);
                    clearBaseModel(e);
                });
            }
        }

        // 获取并处理合同金额详情
        ProjectContractAmountDetailVO projectContractAmount = contractAuditDetailVO.getProjectContractAmount();
        if (projectContractAmount != null) {
            // 清除关联ID以支持复制操作
            clearBaseModel(projectContractAmount);
            projectContractAmount.setCollectedAmount(BigDecimal.ZERO);
            projectContractAmount.setUncollectedAmount(projectContractAmount.getContractSalesTotalAmount());
        }

        // 处理销售项目列表
        List<ProjectContractItemDetailVO> salesItemList = contractAuditDetailVO.getSalesItemList();
        if (salesItemList != null) {
            // 清除列表项的ID以支持复制操作
            salesItemList.forEach(this::clearBaseModel);
        }

        // 处理自制项目列表
        List<ProjectContractItemDetailVO> homemadeItemList = contractAuditDetailVO.getHomemadeItemList();
        if (homemadeItemList != null) {
            // 清除列表项的ID以支持复制操作
            homemadeItemList.forEach(this::clearBaseModel);
        }

        // 处理合同项目列表
        List<ProjectContractItemDetailVO> contractItemList = contractAuditDetailVO.getContractItemList();
        if (contractItemList != null) {
            // 清除列表项的ID以支持复制操作
            contractItemList.forEach(this::clearBaseModel);
        }

        // 处理无成本项目列表
        List<ProjectContractItemDetailVO> noCostItemList = contractAuditDetailVO.getNoCostItemList();
        if (noCostItemList != null) {
            // 清除列表项的ID以支持复制操作
            noCostItemList.forEach(this::clearBaseModel);
        }

        List<ProjectPaymentCollectionPlanDetailVO> planList = contractAuditDetailVO.getPlanList();
        if (planList != null) {
            planList.forEach(this::clearBaseModel);
        }

        // 返回处理后的合同评审详细信息
        return contractAuditDetailVO;
    }

    public void clearBaseModel(BaseModel entity) {
        entity.setId(null);
        entity.setCreateBy(null);
        entity.setCreateTime(null);
        entity.setUpdateBy(null);
        entity.setUpdateTime(null);
    }


    /**
     * 合同变更提交
     * 该方法用于处理合同的变更提交，包括记录合同变更的历史版本、更新合同变更详情等
     * 只有在合同没有处于特定审核状态时，才能进行变更提交
     *
     * @param updatedContract 更新后项目合同评审详情 VO，包含合同变更信息
     * @param submit          是否提交变更标志，true为提交，false为不提交
     * @return JsonResult<String>  返回操作结果
     */
    @Override
    @Transactional
    public JsonResult<String> changeOfContract(ProjectContractAuditDetailVO updatedContract, Boolean submit) {
        // 检查合同状态是否允许取消或变更，如果正处于合同评审或合同变更评审中，则不允许进行变更
//        if (updatedContract.getCate() != 1 && !ContractAuditStatusEnum.canUpdate(updatedContract.getContractStatus())) {
//            return JsonResult.FAIL_VALIDATION("正在进行合同评审或合同变更评审，变更失败！");
//        }
        // 获取合同变更详情，为接下来的变更操作做准备
        ProjectContractUpdateDetailVO projectContractUpdate = updatedContract.getProjectContractUpdate();
        // 如果合同变更信息为空，则返回错误提示
        if (projectContractUpdate == null) {
            return JsonResult.FAIL_EXCEPTION("合同变更信息不能为空");
        }
        // 更新前版本合同信息
        ProjectContractAuditDetailVO currentContract = null;
        // 如果合同变更ID为空，表示没有暂存过
        if (S.isBlank(projectContractUpdate.getId())) {
            // 获取前端修改前合同信息
            currentContract = getViewObject(updatedContract.getId(), ProjectContractAuditDetailVO.class);
            // 更新合同版本号，每次变更提交后，合同版本号递增
            updatedContract.setContractVersion(updatedContract.getContractVersion() + 1);
            this.clear(updatedContract);
            updatedContract.setOaStatus(OAStatusEnum.NO.getCode());
            updatedContract.setSamOaMsg(null);
            updatedContract.setSamOaStatus(OAStatusEnum.NO.getCode());
            updatedContract.setOaMsg(null);
        } else { // 暂存过 修改前数据为上版本
            QueryWrapper<ProjectContractAudit> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(ProjectContractAudit::getContractCode, updatedContract.getContractCode())
                    .eq(ProjectContractAudit::getContractVersion, updatedContract.getContractVersion() - 1);
            ProjectContractAuditDetailVO viewObject = this.getViewObject(queryWrapper, ProjectContractAuditDetailVO.class);
            if (viewObject != null) {
                currentContract = viewObject;
            } else {
                currentContract = getViewObject(updatedContract.getId(), ProjectContractAuditDetailVO.class);
            }
        }
        // 如果是提交变更，则更新历史版本的状态和审核类型
        if (submit) {
            currentContract.setContractStatus(ContractAuditStatusEnum.HISTORY.getCode()).setAuditType("update");
        }
        // 保存历史版本的合同评审详情
        this.saveProjectContractAudit(currentContract);
        // 如果不提交变更，则更新合同状态和审核类型为未提交状态
        if (!submit) {
            updatedContract.setContractStatus(ContractAuditStatusEnum.NO_SUBMIT.getCode()).setAuditType("update");
        } else {
            updatedContract.setContractStatus(ContractAuditStatusEnum.PROCESSING.getCode())
                    .setAuditType("update")
                    .setSapStatus(SAPStatusEnum.NO.getCode()); // 更新SAP推送状态为未推送
        }
        // 更新合同评审详情信息
        this.saveProjectContractAudit(updatedContract);
        if (!submit) {
            return JsonResult.OK(currentContract.getId());
        }
        // 返回操作成功结果
        return JsonResult.OK(updatedContract.getId());
    }

    private void clear(ProjectContractAuditDetailVO updatedContract) {
        updatedContract.setId(null);
        if (updatedContract.getProjectContractAmount() != null) {
            updatedContract.getProjectContractAmount().setId(null);
        }
        if (updatedContract.getProjectContractUpdate() != null) {
            updatedContract.getProjectContractUpdate().setId(null);
        }
        if (updatedContract.getProjectContractCancel() != null) {
            updatedContract.getProjectContractCancel().setId(null);
        }
        if (updatedContract.getPlanList() != null) {
            updatedContract.getPlanList().forEach(e -> e.setId(null));
        }
        if (updatedContract.getContractItemList() != null) {
            updatedContract.getContractItemList().forEach(e -> e.setId(null));
        }
        if (updatedContract.getNoCostItemList() != null) {
            updatedContract.getNoCostItemList().forEach(e -> e.setId(null));
        }
        if (updatedContract.getSalesItemList() != null) {
            updatedContract.getSalesItemList().forEach(e -> e.setId(null));
        }
        if (updatedContract.getHomemadeItemList() != null) {
            updatedContract.getHomemadeItemList().forEach(e -> e.setId(null));
        }
        if (updatedContract.getProjectSamContract() != null) {
            updatedContract.getProjectSamContract().setContractId(null).setId(null);
        }
    }


    /**
     * 获取暂存的合同变更详情
     *
     * @param id 合同审核ID
     * @return 返回合同变更详情的JSON结果
     */
    @Override
    public JsonResult<?> getUpdateDetail(String id) {
        // 根据ID获取当前合同审核实体
        ProjectContractAudit entity = getEntity(id);

        // 创建查询条件封装对象
        QueryWrapper<ProjectContractAudit> queryWrapper = new QueryWrapper<>();
        // 获取暂存的合同变更
        queryWrapper.lambda().eq(ProjectContractAudit::getContractCode, entity.getContractCode())
                .eq(ProjectContractAudit::getContractVersion, entity.getContractVersion() + 1)
                .eq(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode())
                .eq(ProjectContractAudit::getAuditType, "update");

        // 根据查询条件获取合同变更详情视图对象
        ProjectContractAuditDetailVO viewObject = getViewObject(queryWrapper, ProjectContractAuditDetailVO.class);
        // 没有暂存的合同变更详情
        if (viewObject == null) {
            ProjectContractAuditDetailVO contractAuditDetailVO = Binder.convertAndBindRelations(entity, ProjectContractAuditDetailVO.class);
            // 创建一个空的合同变更详情对象
            contractAuditDetailVO.setProjectContractUpdate(new ProjectContractUpdateDetailVO());
            return JsonResult.OK(contractAuditDetailVO);
        }
        // 返回查询结果，若存在则为合同变更详情
        return JsonResult.OK(viewObject);
    }


    /**
     * 审核合同并归档
     * 该方法通过更新数据库中的合同状态为已归档来实现合同的归档操作
     *
     * @param id 合同的唯一标识符
     * @return 返回一个JsonResult实例，表示操作的结果
     */
    @Override
    @Transactional
    public JsonResult<?> archiving(String id) {
        ProjectContractAudit entity = getEntity(id);
        if (!ContractAuditStatusEnum.canArchive(entity.getContractStatus())) {
            return JsonResult.FAIL_VALIDATION("合同未完成，不允许归档！");
        }
        QueryWrapper<ProjectContractAmount> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ProjectContractAmount::getContractId, entity.getId());
        ProjectContractAmount amount = projectContractAmountService.getSingleEntity(queryWrapper);
        if (!NumberUtil.equals(amount.getUncollectedAmount(), BigDecimal.ZERO)) {
            return JsonResult.FAIL_VALIDATION("收款未完成，不允许归档！");
        }
        // 创建一个更新条件封装对象
        UpdateWrapper<ProjectContractAudit> uw = new UpdateWrapper<>();
        // 设置更新条件：根据$id$将合同状态更新为已归档
        uw.lambda().set(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.ARCHIVED.getCode())
                .eq(ProjectContractAudit::getId, id);
        // 执行更新操作
        this.updateEntity(uw);
        // 返回操作成功的结果
        return JsonResult.OK();
    }

    /**
     * 取消合同审批流程
     *
     * @param projectContractAudit 合同审批详情对象，包含合同取消信息
     * @param submit               是否提交审批流程。如果为true，将合同状态设置为处理中并设置审批类型为取消
     * @return JsonResult<?> 表示操作的结果，如果合同取消信息为空，则返回失败并带验证错误信息
     */
    @Override
    @Transactional
    public JsonResult<?> cancelContract(ProjectContractAuditDetailVO projectContractAudit, boolean submit) {
        // 从审批详情中获取合同取消信息
        ProjectContractCancelDetailVO projectContractCancel = projectContractAudit.getProjectContractCancel();
        // 检查合同取消信息是否为空，如果为空则返回失败结果，提示合同取消信息不能为空
        if (projectContractCancel == null) {
            return JsonResult.FAIL_VALIDATION("合同取消信息不能为空");
        }
        // 设置合同取消信息中的合同ID
        projectContractCancel.setContractId(projectContractAudit.getId());
        // 如果提交参数为true，更新合同审批状态为处理中，并设置审批类型为取消
        if (submit) {
            projectContractAudit.setAuditType("cancel").setOaStatus(OAStatusEnum.NO.getCode());
        }
        // 更新合同审批实体
        this.updateEntity(projectContractAudit);
        // 调用服务保存或更新合同取消实体
        projectContractCancelService.createOrUpdateEntity(projectContractCancel);
        // 推送oa
        ProjectContractAuditDetailVO viewObject = this.getViewObject(projectContractAudit.getId(), ProjectContractAuditDetailVO.class);
        new Thread(() -> {
            pushOa(viewObject, "proj");
        }).start();
        return JsonResult.OK();
    }


    /**
     * 查询合同审批列表
     *
     * @param queryDto   查询条件对象
     * @param pagination 分页对象
     * @param type       类型
     * @param permission 权限
     * @return JsonResult<List < ProjectContractAuditListVO>> 返回审批列表的JSON结果
     */
    @Override
    public JsonResult<List<ProjectContractAuditListVO>> selectContractAudit(ProjectContractAuditDTO queryDto, Pagination pagination, String type, String permission) {
        // 根据查询条件和分页信息生成查询包装器
        QueryWrapper<ProjectContractAudit> queryWrapper = QueryBuilder.toQueryWrapper(queryDto, pagination);
        queryWrapper.lambda().orderBy(true, false, ProjectContractAudit::getId);
        // 根据类型设置查询条件
        if (S.equals(type, "draft")) { // 草稿类型
            queryWrapper.lambda()
                    .eq(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode())
                    .eq(ProjectContractAudit::getCreateBy, IamSecurityUtils.getCurrentUserId())
                    .eq(ProjectContractAudit::getAuditType, "create");
        } else if (S.equals(type, "formal")) { // 正式类型
            queryWrapper.lambda()
                    .ne(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode())
                    .ne(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode())
                    .ne(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.ARCHIVED.getCode());
        }

        if (S.equals(queryDto.getAuditType(), ContractAuditTypeEnum.UPDATE.getCode()) || S.equals(queryDto.getAuditType(), ContractAuditTypeEnum.CANCEL.getCode())) {
            queryWrapper.lambda().ne(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode())
                    .ne(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.ARCHIVED.getCode())
                    .ne(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.NO_SUBMIT.getCode());
        }
        if (!commonService.isCrossTenant(IamSecurityUtils.getCurrentTenantId())) {
            // 根据权限设置查询条件
            if (S.equals(permission, "self")) { // 本人权限
                queryWrapper.lambda()
                        .eq(ProjectContractAudit::getCreateBy, IamSecurityUtils.getCurrentUserId())
                        .or().in(ProjectContractAudit::getId, contractTeamService.getTeamContract(Collections.singletonList(IamSecurityUtils.getCurrentUserId())));
            } else if (S.equals(permission, "sub")) { // 下属权限
                List<String> subEmp = commonService.findSubEmp(IamSecurityUtils.getCurrentUser());
                if (subEmp.isEmpty()) {
                    return JsonResult.OK(new ArrayList<>());
                }
                queryWrapper.lambda().in(ProjectContractAudit::getCreateBy, subEmp)
                        .or().in(ProjectContractAudit::getId, contractTeamService.getTeamContract(subEmp));
            } else if (S.equals(permission, "star")) { // 收藏权限
                List<String> starContractIds = projectContractAuditMapper.getStarContractIds(IamSecurityUtils.getCurrentUserId());
                if (starContractIds.isEmpty()) {
                    return JsonResult.OK(new ArrayList<>());
                }
                queryWrapper.lambda().in(ProjectContractAudit::getId, starContractIds);
            } else if (S.equals(permission, "all")) { // 本人及下属权限
                List<String> subEmp = commonService.findSubEmp(IamSecurityUtils.getCurrentUser());
                subEmp.add(IamSecurityUtils.getCurrentUserId());
                queryWrapper.lambda().in(ProjectContractAudit::getCreateBy, subEmp)
                        .or().in(ProjectContractAudit::getId, contractTeamService.getTeamContract(subEmp));
            }
        }
        // 查询审批列表视图对象列表
        List<ProjectContractAuditListVO> viewObjectList = getViewObjectList(queryWrapper, pagination, ProjectContractAuditListVO.class);
        // 遍历视图对象列表，判断当前用户是否收藏了该合同，如果是，则设置收藏标志为true
        for (ProjectContractAuditListVO projectContractAuditListVO : viewObjectList) {
            List<String> starUids = projectContractAuditListVO.getStarUids();
            if (starUids != null && starUids.contains(IamSecurityUtils.getCurrentUserId())) {
                projectContractAuditListVO.setIsStar(true);
            }
        }
        // 根据查询包装器、分页信息和视图对象类型获取审批列表视图对象，并返回JSON结果
        return JsonResult.OK(viewObjectList).bindPagination(pagination);
    }

    /**
     * 比较合同修改前后的变化情况
     *
     * @param projectContractAudit 修改合同信息
     * @param id                   合同ID
     * @return
     */
    @Override
    public Map<String, Object> compareChange(ProjectContractAuditDetailVO projectContractAudit, String id) {
        // 收集新合同的项目信息
        List<ProjectContractItemDetailVO> newList = projectContractAudit.collectItemList();

        // 获取旧合同信息
        ProjectContractAuditDetailVO contractDetailVO = this.getViewObject(id, ProjectContractAuditDetailVO.class);

        // 收集旧合同的项目信息
        List<ProjectContractItemDetailVO> oldList = contractDetailVO.collectItemList();

        Map<String, Object> contractAmountMap = compareContractAmount(projectContractAudit, contractDetailVO);
        List<ProjectDiffVO> diffVOS = compareChange(newList, oldList);
        Map<String, Object> res = new HashMap<>();
        res.put("item", diffVOS);
        res.put("contract", contractAmountMap);
        return res;
    }

    /**
     * 比较合同金额修改前后的变化情况
     *
     * @param projectContractAudit
     * @param contractDetailVO
     * @return
     */
    private Map<String, Object> compareContractAmount(ProjectContractAuditDetailVO projectContractAudit, ProjectContractAuditDetailVO contractDetailVO) {
        ProjectContractAmountDetailVO newAmount = projectContractAudit.getProjectContractAmount();
        ProjectContractAmountDetailVO oldAmount = contractDetailVO.getProjectContractAmount();
        // 比较采购总金额，销售总金额，折扣
        Map<String, Object> amountMap = new HashMap<>();
        if (!NumberUtil.equals(newAmount.getContractProjectPurchaseTotal(), oldAmount.getContractProjectPurchaseTotal()) ||
                !NumberUtil.equals(newAmount.getContractProjectSaleTotal(), oldAmount.getContractProjectSaleTotal()) ||
                !NumberUtil.equals(newAmount.getDiscount(), oldAmount.getDiscount())) {
            amountMap.put("beforeContractProjectPurchaseTotal", oldAmount.getContractProjectPurchaseTotal());
            amountMap.put("beforeContractProjectSaleTotal", oldAmount.getContractProjectSaleTotal());
            amountMap.put("beforeDiscount", oldAmount.getDiscount());
            amountMap.put("beforeRemark", oldAmount.getRemark());
            amountMap.put("afterContractProjectPurchaseTotal", newAmount.getContractProjectPurchaseTotal());
            amountMap.put("afterContractProjectSaleTotal", newAmount.getContractProjectSaleTotal());
            amountMap.put("afterDiscount", newAmount.getDiscount());
            amountMap.put("afterRemark", newAmount.getRemark());
        }
        return amountMap;
    }

    public List<ProjectContractItem> compareDelChange(List<ProjectContractItemDetailVO> newList, List<ProjectContractItemDetailVO> oldList) {
        // 初始化差异列表
        List<ProjectContractItem> diffVOS = new ArrayList<>();
        // 将新合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> newMap = newList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 将旧合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> oldMap = oldList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 计算新合同中新增加的键集
        Set<String> addedKeys = new HashSet<>(newMap.keySet());
        addedKeys.removeAll(oldMap.keySet());

        // 遍历新增加的键集，将新增的项目信息添加到差异列表
        for (String addedKey : addedKeys) {
            ProjectContractItemDetailVO itemDetailVO = newMap.get(addedKey);
            diffVOS.add(itemDetailVO);
        }

        // 返回差异列表
        return diffVOS;

    }

    public List<ProjectContractItem> compareAddChange(List<ProjectContractItemDetailVO> newList, List<ProjectContractItemDetailVO> oldList) {
        // 初始化差异列表
        List<ProjectContractItem> diffVOS = new ArrayList<>();
        // 将新合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> newMap = newList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 将旧合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> oldMap = oldList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 计算旧合同中已移除的键集
        Set<String> removedKeys = new HashSet<>(oldMap.keySet());
        removedKeys.removeAll(newMap.keySet());

        // 遍历已移除的键集，将移除的项目信息添加到差异列表
        for (String removedKey : removedKeys) {
            ProjectContractItemDetailVO itemDetailVO = oldMap.get(removedKey);
            diffVOS.add(itemDetailVO);
        }

        // 返回差异列表
        return diffVOS;
    }


    /**
     * 比较合同修改前后的变化情况
     *
     * @param newList
     * @param oldList
     * @return
     */
    public List<ProjectDiffVO> compareChange(List<ProjectContractItemDetailVO> newList, List<ProjectContractItemDetailVO> oldList) {
        // 初始化差异列表
        List<ProjectDiffVO> diffVOS = new ArrayList<>();

        // 将新合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> newMap = newList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 将旧合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> oldMap = oldList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 计算两个Map的共有键集
        Set<String> commonKeys = new HashSet<>(newMap.keySet());
        commonKeys.retainAll(oldMap.keySet());

        // 计算旧合同中已移除的键集
        Set<String> removedKeys = new HashSet<>(oldMap.keySet());
        removedKeys.removeAll(newMap.keySet());

        // 计算新合同中新增加的键集
        Set<String> addedKeys = new HashSet<>(newMap.keySet());
        addedKeys.removeAll(oldMap.keySet());

        // 遍历共有键集，比较项目信息的差异
        for (String commonKey : commonKeys) {
            ProjectContractItemDetailVO itemDetailVO = newMap.get(commonKey);
            ProjectContractItemDetailVO oldItemDetailVO = oldMap.get(commonKey);

            // 如果项目名称、物料编码或数量有变化，则添加到差异列表
            if (!S.equals(itemDetailVO.getEquipmentName(), oldItemDetailVO.getEquipmentName()) || !S.equals(itemDetailVO.getMaterialCode(), oldItemDetailVO.getMaterialCode()) || !NumberUtil.equals(itemDetailVO.getQuantity(), oldItemDetailVO.getQuantity())) {
                diffVOS.add(new ProjectDiffVO(
                        ItemtypeEnum.getDesc(itemDetailVO.getItemType()),
                        toStr(oldItemDetailVO.getSerialNumber(), ""),
                        oldItemDetailVO.getEquipmentName(),
                        oldItemDetailVO.getMaterialCode(),
                        toStr(oldItemDetailVO.getQuantity(), ""),
                        null,
                        toStr(itemDetailVO.getSerialNumber(), ""),
                        itemDetailVO.getEquipmentName(),
                        itemDetailVO.getMaterialCode(),
                        toStr(itemDetailVO.getQuantity(), ""),
                        null
                ));
            }
        }

        // 遍历新增加的键集，将新增的项目信息添加到差异列表
        for (String addedKey : addedKeys) {
            ProjectContractItemDetailVO itemDetailVO = newMap.get(addedKey);
            diffVOS.add(new ProjectDiffVO(
                    ItemtypeEnum.getDesc(itemDetailVO.getItemType()),
                    null,
                    null,
                    null,
                    null,
                    null,
                    toStr(itemDetailVO.getSerialNumber(), ""),
                    itemDetailVO.getEquipmentName(),
                    itemDetailVO.getMaterialCode(),
                    toStr(itemDetailVO.getQuantity(), ""),
                    "新增"
            ));
        }

        // 遍历已移除的键集，将移除的项目信息添加到差异列表
        for (String removedKey : removedKeys) {
            ProjectContractItemDetailVO itemDetailVO = oldMap.get(removedKey);
            diffVOS.add(new ProjectDiffVO(
                    ItemtypeEnum.getDesc(itemDetailVO.getItemType()),
                    toStr(itemDetailVO.getSerialNumber(), ""),
                    itemDetailVO.getEquipmentName(),
                    itemDetailVO.getMaterialCode(),
                    toStr(itemDetailVO.getQuantity(), ""),
                    "删除",
                    null,
                    null,
                    null,
                    null,
                    ""
            ));
        }

        // 返回差异列表
        return diffVOS;
    }

    /**
     * 比较合同修改前后的项信息变化情况
     *
     * @param id 当前合同id
     * @return
     */
    public Map<String, List<ProjectContractItem>> compareItemChange(String id) {
        ProjectContractAuditDetailVO nowContract = this.getViewObject(id, ProjectContractAuditDetailVO.class);
        Binder.bindRelations(nowContract);
        QueryWrapper<ProjectContractAudit> qw = new QueryWrapper<>();
        qw.lambda().eq(ProjectContractAudit::getContractCode, nowContract.getContractCode())
                .eq(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode())
                .eq(ProjectContractAudit::getContractVersion, nowContract.getContractVersion() - 1);
        ProjectContractAuditDetailVO lastContract = this.getViewObject(qw, ProjectContractAuditDetailVO.class);
        if (lastContract == null) {
            return new HashMap<>();
        }
        List<ProjectContractItemDetailVO> newList = nowContract.collectItemList();
        List<ProjectContractItemDetailVO> oldList = lastContract.collectItemList();
        // 初始化差异列表
        List<ProjectContractItem> addList = new ArrayList<>();
        List<ProjectContractItem> delList = new ArrayList<>();

        // 将新合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> newMap = newList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 将旧合同的项目信息转换为Map，键为序列号和物料类型的组合
        Map<String, ProjectContractItemDetailVO> oldMap = oldList.stream().collect(Collectors.toMap(e -> e.getSerialNumber() + "-" + e.getItemType(), Function.identity()));

        // 计算两个Map的共有键集
        Set<String> commonKeys = new HashSet<>(newMap.keySet());
        commonKeys.retainAll(oldMap.keySet());

        // 计算旧合同中已移除的键集
        Set<String> removedKeys = new HashSet<>(oldMap.keySet());
        removedKeys.removeAll(newMap.keySet());

        // 计算新合同中新增加的键集
        Set<String> addedKeys = new HashSet<>(newMap.keySet());
        addedKeys.removeAll(oldMap.keySet());

        // 遍历新增加的键集，将新增的项目信息添加到差异列表
        for (String addedKey : addedKeys) {
            ProjectContractItemDetailVO itemDetailVO = newMap.get(addedKey);
            addList.add(itemDetailVO);
        }

        // 遍历已移除的键集，将移除的项目信息添加到差异列表
        for (String removedKey : removedKeys) {
            ProjectContractItemDetailVO itemDetailVO = oldMap.get(removedKey);
            delList.add(itemDetailVO);
        }


//        List<ProjectContractItem> diffVOS = compareAddChange(nowContract.collectItemList(), lastContract.collectItemList());
//        List<ProjectContractItem> del = compareDelChange(nowContract.collectItemList(), lastContract.collectItemList());
        Map<String, List<ProjectContractItem>> res = new HashMap<>();
        res.put("add", addList);
        res.put("del", delList);
        return res;
    }

    /**
     * 比较合同修改前后的变化情况
     *
     * @param id
     * @return
     */
    @Override
    public Map<String, Object> compareChange(String id) {
        ProjectContractAuditDetailVO nowContract = this.getViewObject(id, ProjectContractAuditDetailVO.class);
        Binder.bindRelations(nowContract);
        QueryWrapper<ProjectContractAudit> qw = new QueryWrapper<>();
        qw.lambda().eq(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode())
                .eq(ProjectContractAudit::getContractCode, nowContract.getContractCode())
                .eq(ProjectContractAudit::getContractVersion, nowContract.getContractVersion() - 1);
        ProjectContractAuditDetailVO lastContract = this.getViewObject(qw, ProjectContractAuditDetailVO.class);
        if (lastContract == null) {
            return null;
        }
        Map<String, Object> map = compareContractAmount(nowContract, lastContract);
        List<ProjectDiffVO> diffVOS = compareChange(nowContract.collectItemList(), lastContract.collectItemList());
        Map<String, Object> res = new HashMap<>();
        res.put("item", diffVOS);
        res.put("contract", map);
        return res;
    }

    @Override
    public Map<String, Object> compareChange(ProjectContractAuditDetailVO data) {
        QueryWrapper<ProjectContractAudit> qw = new QueryWrapper<>();
        qw.lambda().eq(ProjectContractAudit::getContractCode, data.getContractCode())
                .eq(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode())
                .eq(ProjectContractAudit::getContractVersion, data.getContractVersion() - 1);
        ProjectContractAuditDetailVO lastContract = this.getViewObject(qw, ProjectContractAuditDetailVO.class);
        Map<String, Object> map = compareContractAmount(data, lastContract);
        List<ProjectDiffVO> diffVOS = compareChange(data.collectItemList(), lastContract.collectItemList());
        Map<String, Object> res = new HashMap<>();
        res.put("item", diffVOS);
        res.put("contract", map);
        return res;
    }

    @Override
    public List<ProjectContractAuditListVO> getHistory(String code, Pagination pagination) {
        QueryWrapper<ProjectContractAudit> qw = new QueryWrapper<>();
        qw.lambda().eq(ProjectContractAudit::getContractCode, code)
                .eq(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.HISTORY.getCode());
        return getViewObjectList(qw, pagination, ProjectContractAuditListVO.class);
    }


    @Override
    public JsonResult<String> receiveOaApprovalAuditResults(String objectid, String result, String bdcode) {
        ThreadLocalHolder.setIgnoreInterceptor();
        ProjectContractAuditDetailVO contract = this.getViewObject(objectid, ProjectContractAuditDetailVO.class);
        // 供销合同审批
        if (S.contains("sam", bdcode)) {
            if (S.equals(result, "已完成")) {
                contract.setContractStatus(ContractAuditStatusEnum.OA_SUCCESS.getCode())
                        .setSamOaStatus(OAStatusEnum.RESP.getCode())
                        .setSamOaMsg(result);
            } else {
                contract.setContractStatus(ContractAuditStatusEnum.OA_FAIL.getCode())
                        .setSamOaStatus(OAStatusEnum.RESP.getCode())
                        .setSamOaMsg(result);
            }
        } else {
            if (S.equals(result, "已完成")) {
                contract.setContractStatus(ContractAuditStatusEnum.OA_SUCCESS.getCode())
                        .setOaStatus(OAStatusEnum.RESP.getCode())
                        .setOaMsg(result)
                        .setStatus("2");
            } else {
                contract.setContractStatus(ContractAuditStatusEnum.OA_FAIL.getCode())
                        .setOaStatus(OAStatusEnum.RESP.getCode())
                        .setOaMsg(result);
            }
        }
        if (S.equals(result, "已完成") && S.contains(bdcode, ContractAuditTypeEnum.CANCEL.getCode())) {
            String sapContractCode = contract.getSapContractCode();
            contract.setContractStatus(ContractAuditStatusEnum.CANCEL.getCode());
            if (S.isNotBlank(sapContractCode)) {
                QueryWrapper<SalesContract> qw = new QueryWrapper<>();
                qw.lambda().select(SalesContract::getId, SalesContract::getSapCode).eq(SalesContract::getSapCode, sapContractCode);
                ThreadLocalHolder.setIgnoreInterceptor();
                SalesContract salesContract = salesContractMapper.selectOne(qw);
                try {
                    if (salesContract != null && S.isNotEmpty(salesContract.getSapCode())) {
                        salesContractService.cancelSap(salesContract.getId());
                    }
                } catch (JCoException e) {
                    log.error("{} 销售合同取消失败 {}", sapContractCode, e.getMessage());
                    throw new RuntimeException(e);
                }
            }
        }
        ThreadLocalHolder.setIgnoreInterceptor();
        this.updateEntity(contract);
        JsonResult<String> jsonResult = new JsonResult<>();
        jsonResult.setCode(200);
        jsonResult.setMsg("操作成功");
        return jsonResult;
    }

    @Override
    public void delete(String id) {
        ProjectContractAuditDetailVO contractAuditDetailVO = this.getViewObject(id, ProjectContractAuditDetailVO.class);
        if (!ContractAuditStatusEnum.canDelete(contractAuditDetailVO.getContractStatus())) {
            throw new BusinessException("合同已提交，无法删除");
        }
        ProjectSamContractDetailVO projectSamContract = contractAuditDetailVO.getProjectSamContract();
        List<ProjectPaymentCollectionPlanDetailVO> planList = contractAuditDetailVO.getPlanList();
        List<ProjectContractItemDetailVO> noCostItemList = contractAuditDetailVO.getNoCostItemList();
        List<ProjectContractItemDetailVO> salesItemList = contractAuditDetailVO.getSalesItemList();
        List<ProjectContractItemDetailVO> homemadeItemList = contractAuditDetailVO.getHomemadeItemList();
        List<ProjectContractItemDetailVO> contractItemList = contractAuditDetailVO.getContractItemList();
        ProjectContractUpdateDetailVO projectContractUpdate = contractAuditDetailVO.getProjectContractUpdate();
        ProjectContractCancelDetailVO projectContractCancel = contractAuditDetailVO.getProjectContractCancel();
        this.deleteEntity(id);
        if (projectContractCancel != null) {
            projectContractUpdateService.deleteEntity(projectContractUpdate.getId());
        }
        if (projectSamContract != null) {
            projectSamContractService.deleteEntity(projectSamContract.getId());
        }
        if (planList != null) {
            projectPaymentCollectionPlanService.deleteEntities(planList);
        }
        if (noCostItemList != null) {
            projectContractItemService.deleteEntities(noCostItemList);
        }
        if (salesItemList != null) {
            projectContractItemService.deleteEntities(salesItemList);
        }
        if (homemadeItemList != null) {
            projectContractItemService.deleteEntities(homemadeItemList);
        }
        if (contractItemList != null) {
            projectContractItemService.deleteEntities(contractItemList);
        }
    }

    @Override
    public JsonResult<?> cancelContract(ProjectContractCancelDetailVO projectContractCancel, boolean submit) {
        ProjectContractAuditDetailVO contract = this.getViewObject(projectContractCancel.getContractId(), ProjectContractAuditDetailVO.class);
        if (contract.getCate() != 1 && !ContractAuditStatusEnum.canCancel(contract.getContractStatus())) {
            return JsonResult.FAIL_OPERATION("合同状态不允许取消");
        }
        contract.setProjectContractCancel(projectContractCancel);
        return this.cancelContract(contract, submit);
    }

    @Override
    public JsonResult<?> cancelArchiving(String id) {
        // 创建一个更新条件封装对象
        UpdateWrapper<ProjectContractAudit> uw = new UpdateWrapper<>();
        // 设置更新条件：根据$id$将合同状态更新
        uw.lambda().set(ProjectContractAudit::getContractStatus, ContractAuditStatusEnum.OA_SUCCESS.getCode())
                .eq(ProjectContractAudit::getId, id);
        // 执行更新操作
        this.updateEntity(uw);
        // 返回操作成功的结果
        return JsonResult.OK();
    }

    /**
     * 打印合同
     *
     * @param id
     * @return
     */
    @Transactional(readOnly = true)
    @Override
    public String printWord(String id) {
        // 查询合同
        ProjectContractAuditDetailVO contract = this.getViewObject(id, ProjectContractAuditDetailVO.class);
        if (contract == null) {
            throw new BusinessException("未找到合同");
        }
        if (!S.equals(contract.getContractStatus(), ContractAuditStatusEnum.OA_SUCCESS.getCode())
                && !S.equals(contract.getContractStatus(), ContractAuditStatusEnum.DONE.getCode())
                && !S.equals(contract.getContractStatus(), ContractAuditStatusEnum.ARCHIVED.getCode())
                && !S.equals(contract.getContractStatus(), ContractAuditStatusEnum.CANCEL.getCode())
        ) {
            throw new BusinessException("合同未通过审批");
        }
        String tenantId = contract.getTenantId();
        IamTenant tenant = iamTenantMapper.selectById(tenantId);
        String tempFileName;
        Map<String, Object> params;
        if (S.equals(tenant.getCode(), "skxt") || S.equals(tenant.getCode(), "zxzhb")) {
            tempFileName = "word/proj-audit-cnc.docx";
            params = buildPrintParamsCnc(contract);
        } else {
            tempFileName = "word/proj-audit.docx";
            params = buildPrintParamsIs(contract);
        }
        String resourceFilePath = WordUtils.getResourceFilePath(tempFileName);
        String tempDir = WordUtils.getTemplateFilePath() + contract.getContractCode() + "/";
        File td = new File(tempDir);
        if (td.exists()) {
            try {
                FileUtils.deleteDirectory(td);
            } catch (IOException e) {
                throw new com.diboot.core.exception.BusinessException(Status.FAIL_EXCEPTION, e);
            }
        }
        boolean mkdir = td.mkdir();
        if (!mkdir) {
            throw new com.diboot.core.exception.BusinessException("创建临时文件夹失败");
        }
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        Configure conf = Configure.builder().bind("items", policy).bind("planList", policy).build();
        try (XWPFTemplate template = XWPFTemplate.compile(resourceFilePath, conf).render(params); FileOutputStream fos = new FileOutputStream(tempDir + contract.getContractCode() + "项目评审报告单.docx")) {
            template.write(fos);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 供销合同打印
        if (contract.getProjectSamContract() != null && contract.getProjectSamContract().getCompanyContractTemplate()) {
            String samTempPath = WordUtils.getResourceFilePath("word/proj-sam-contract.docx");
            LoopRowTableRenderPolicy policy2 = new LoopRowTableRenderPolicy();
            Configure conf2 = Configure.builder().bind("equipmentList", policy2).build();
            ProjectSamContractDetailVO samContract = contract.getProjectSamContract();
            Map<String, Object> param2 = BeanUtil.beanToMap(samContract);
            param2.put("nine", getNine(samContract.getAdvancePayment()));
            param2.put("equipmentList", samContract.getEquipmentDetailVOS());
            param2.put("contractCode", contract.getContractCode());
            LocalDateTime signingTime = samContract.getSigningTime() == null ? contract.getCreateTime() == null ? LocalDateTime.now() : contract.getCreateTime() : samContract.getSigningTime();
            param2.put("signYear", signingTime.getYear());
            param2.put("signMonth", signingTime.getMonthValue());
            param2.put("signDay", signingTime.getDayOfMonth());
            param2.put("payMethod", getPayMethod(contract));
            param2.put("totalUppercase", samContract.getTotal() == null ? "" : NumberChineseFormatter.format(samContract.getTotal().doubleValue(), true, true));
            try (XWPFTemplate template = XWPFTemplate.compile(samTempPath, conf2).render(param2); FileOutputStream fos = new FileOutputStream(tempDir + contract.getContractCode() + "产品供销合同.docx")) {
                template.write(fos);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        // 压缩文件
        String dir = tempDir.substring(0, tempDir.lastIndexOf("/"));
        String zipPath = dir + ".zip";
        try (FileOutputStream zipOut = new FileOutputStream(zipPath);
             ZipOutputStream zipOutputStream = new ZipOutputStream(zipOut)) {
            WordUtils.addFolderToZip("", dir, zipOutputStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 返回压缩文件路径
        return zipPath;
    }

    /**
     * 构建打印参数 数控事业部
     *
     * @param contract
     * @return
     */
    public Map<String, Object> buildPrintParamsCnc(ProjectContractAuditDetailVO contract) {
        Map<String, Object> params = new HashMap<>();
        params.put("contractType", contract.getProjectTypeLabel() == null ? "" : contract.getProjectTypeLabel().getLabel());
        params.put("contractCode", contract.getContractCode());
        params.put("customerName", contract.getCustomerName());
        params.put("cutomerCate", contract.getCustomerNatureLabel() == null ? "" : contract.getCustomerNatureLabel().getLabel());
        params.put("signerName", contract.getSalerName());
        params.put("signDept", contract.getBusinessDeptLabel() == null ? "" : contract.getBusinessDeptLabel().getLabel());
        params.put("area", contract.getAreaLabel() == null ? "" : contract.getAreaLabel().getLabel());
        params.put("auditTime", DateUtil.format(contract.getAuditTime(), "yyyy-MM-dd"));
        params.put("finalUser", contract.getFinalUser());
        List<Map<String, Object>> maps = processPrintList(contract);
        params.put("items", maps);
        params.put("contractSalesTotalAmount", contract.getProjectContractAmount().getContractSalesTotalAmount());
        params.put("contractPurchaseTotalAmount", contract.getProjectContractAmount().getContractPurchaseTotalAmount());
        params.put("discount", contract.getProjectContractAmount().getDiscount());
        ProjectSamContractDetailVO projectSamContract = contract.getProjectSamContract();
        if (projectSamContract != null) {
            params.put("paymentMethodStr", getPayMethod(contract));
            //params.put("nine",getNine(projectSamContract.getAdvancePayment()));
            if (projectSamContract.getDeliveryTimeLabel() != null) {
                params.put("deliveryDateStr", String.format("双方盖章甲方支付预付款后合同生效，%s，%s日内交货。"
                        , projectSamContract.getDeliveryTimeLabel().getLabel(), projectSamContract.getDeliveryDuration()));
            } else {
                params.put("deliveryDateStr", contract.getDeliveryDate());
            }
            if (projectSamContract.getCompanyContractTemplate()) {
                params.put("beforeDate",
                        String.format("机械设备的质保期为最终用户验收合格之日起%s个月；电气设备的质保期为最终用户验收合格之日起%s个月,在质保期内，因制造商原因所致的设备质量问题，乙方为其免费维修、更换配件；因用户使用不当或超过上述质保期限的维修费和配件费用由甲方承担，乙方提供有偿服务。"
                                , projectSamContract.getMechanicalEquipmentWarranty(), projectSamContract.getElectricalEquipmentWarranty()));
            } else {
                params.put("beforeDate", contract.getWarrantyPeriod());
            }
        }
        return params;
    }

    private String getNine(String val) {
        String nine = """
                2、甲方应于合同盖章之日起 %s 日内支付预付款，逾期超过30日，乙方有权单方面解除合同，甲方应按合同标的额的百分之三十给付乙方违约金。
                3、本合同如有未尽事宜，须经双方共同协商后作出书面补充规定。在合同执行过程中双方发生纠纷应首先通过友好协商解决；协商不成，交由合同签订地人民法院裁决。
                4、本合同经甲方支付预付款后合同生效，一式肆份，双方各执贰份，合同原件及传真件具有同等法律效力。\
                """;
        String nine2 = """
                2、本合同如有未尽事宜，须经双方共同协商后作出书面补充规定。在合同执行过程中双方发生纠纷应首先通过友好协商解决；协商不成，交由合同签订地人民法院裁决。
                3、本合同经甲方支付预付款后合同生效，一式肆份，双方各执贰份，合同原件及传真件具有同等法律效力。\
                """;
        return val == null ? nine2 : String.format(nine, val);
    }

    private List<Map<String, Object>> processPrintList(ProjectContractAuditDetailVO contract) {
        List<ProjectContractItemDetailVO> items = contract.collectItemList();
        items.forEach(el -> el.setItemType(ItemtypeEnum.getDesc(el.getItemType())));
        List<Map<String, Object>> maps = items.stream().map(e -> BeanUtil.beanToMap(e, false, true)).toList();
        maps.forEach(e -> {
            if (e.get("deliveryDate") != null) {
                e.put("deliveryDate", DateUtil.format((LocalDateTime) e.get("deliveryDate"), "yyyy-MM-dd"));
            }
        });
        return maps;
    }

    /**
     * 构建打印参数 国际事业部
     *
     * @param contract
     * @return
     */
    public Map<String, Object> buildPrintParamsIs(ProjectContractAuditDetailVO contract) {
        // 构建参数
        Map<String, Object> params = new HashMap<>();
        params.put("contractType", contract.getProjectTypeLabel() == null ? "" : contract.getProjectTypeLabel().getLabel());
        params.put("contractCode", contract.getContractCode());
        params.put("customerName", contract.getCustomerName());
        params.put("signerName", contract.getSalerName());
        params.put("cutomerCate", contract.getCustomerNatureLabel() == null ? "" : contract.getCustomerNatureLabel().getLabel());
        params.put("sapCode", contract.getContractCode());
        params.put("plannedReleaseTime", contract.getPlannedReleaseTime());
        params.put("plannedDeliveryTime", contract.getPlannedDeliveryTime());
        params.put("contractSalesTotalAmount", contract.getProjectContractAmount().getContractSalesTotalAmount());
        params.put("contractPurchaseTotalAmount", contract.getProjectContractAmount().getContractPurchaseTotalAmount());
        params.put("discount", contract.getProjectContractAmount().getDiscount());
        params.put("requiredCompletionTime", contract.getProjectContractAmount().getRequiredCompletionTime());
        params.put("actualCompletionTime", contract.getProjectContractAmount().getActualCompletionTime());
        List<Map<String, Object>> maps = processPrintList(contract);
        params.put("items", maps);
        params.put("planList", contract.getPlanList());
        return params;
    }


    /**
     * 获取支付方式
     *
     * @param contract
     * @return
     */
    private String getPayMethod(ProjectContractAuditDetailVO contract) {
        ProjectSamContractDetailVO samContract = contract.getProjectSamContract();
        if (samContract == null || !samContract.getCompanyContractTemplate()) {
            return contract.getPaymentMethod();
        }
        return switch (samContract.getPaymentMethod()) {
            // 质保金付款
            case "0" -> String.format("甲乙双方合同签订后%s日付%s，到货后%s日付%s，验收合格后%s日付%s，质保期后付清%s",
                    samContract.getContractSignedPostLimitOfAB(), samContract.getPaymentRatioPostSigning() + "%"
                    , samContract.getPostArrivalLimit(), samContract.getPaymentRatioPostArrival() + "%"
                    , samContract.getPostQualifiedInspectionLimit(), samContract.getPaymentRatioPostQualifiedInspection() + "%"
                    , samContract.getAnnualWarrantyAndThenPayOffAll() + "%");

            // 履约保证金付款
            case "1" ->
                    String.format("甲乙双方签订合同前%s日，乙方向甲方%s日付%s作为履约保证金，乙方提供的货物经验收合格后%s日，甲方支付乙方100%%货款，履约保证金转为质保金于%s年质保期满%s日退回乙方"
                            , samContract.getContractSignedPreLimitOfAB(), samContract.getLimitFromBToA(), samContract.getPaymentRatioFromBToA() + "%"
                            , samContract.getGoodsReceivedQualifiedPostLimitOfB(), samContract.getPerformanceBondToQualityDepositPeriod(), samContract.getQualityGuaranteePeriod());

            // 代理商付款
            case "2" -> String.format("甲方于合同签订后%s日内支付预付款¥%s元；余款¥%s元应于发货前%s日内付清"
                    , samContract.getPartyASignedContractPostLimit(), samContract.getPrepaymentAmount(), samContract.getRemainingAmount(), samContract.getPreDeliveryLimit());
            default -> "";
        };
    }

    /**
     * 获取付款方式
     *
     * @param samContract
     * @return
     */
    public String getPayMethodWithTitle(Object samContract) {
        String payMethod = getPayMethod((ProjectContractAuditDetailVO) samContract);
        String title = "四、付款方式：\n";
        return title + payMethod;
    }

    /**
     * 布尔值转换
     *
     * @param bool
     * @return
     */
    public String boolChange(Object bool) {
        return Objects.equals(bool, true) ? "10" : "20";
    }

    /**
     * 获取合同执行状态
     *
     * @param update
     * @return
     */
    public String getContractExecStatus(Object update) {
        ProjectContractUpdateDetailVO projectContractUpdateDetailVO = (ProjectContractUpdateDetailVO) update;
        String invoiceStatus = projectContractUpdateDetailVO.getInVoicingStatus();
        String sendOutStatus = projectContractUpdateDetailVO.getShippingStatus();
        if (S.equals(invoiceStatus, "2") && S.equals(sendOutStatus, "1")) {  // 未开票未发货
            return "10";
        } else if (!S.equals(invoiceStatus, "2") && !S.equals(sendOutStatus, "1")) { // 已开票已发货
            return "30";
        } else if (S.equals(invoiceStatus, "2") && !S.equals(sendOutStatus, "1")) { // 已发货未开票
            return "20";
        } else {
            return "";
        }
    }


    @Override
    public List<?> importHistoryData(MultipartFile file, Map<String, Object> params) throws IOException, InstantiationException, IllegalAccessException {
        List<HistoricalFinancialDataImportModel> list = multiSheetExcelImportService.readSingleSheet(file, HistoricalFinancialDataImportModel.class, HistoricalFinancialDataImportListener.class);
        if (list == null || list.isEmpty()) {
            return null;
        }
        importFinancialHistoryData(list);
        return null;
    }

    /**
     * 处理并导入
     *
     * @param list
     * @author 陈广煜
     * @date 2025-03-15
     */
    private void importFinancialHistoryData(List<HistoricalFinancialDataImportModel> list) {
        Map<String, String> tenantMap = new HashMap<>();
        tenantMap.put("系统事业部", "1791742964004937730");
        tenantMap.put("系统事业部未开票", "1791742964004937730");
        tenantMap.put("数控系统事业部", "1791742964004937730");
//        tenantMap.put("教育事业部", "");
        tenantMap.put("重大专项指挥部", "1807588210772148225");
        tenantMap.put("国际事业部", "1867464222370451458");
        tenantMap.put("国际事业部未开票", "1867464222370451458");
        tenantMap.put("红外", "1876168364448530433");
        tenantMap.put("红外未开票", "1876168364448530433");
        tenantMap.put("重大专项组本部", "1807588210772148225");
        tenantMap.put("重大专项组本部未开票", "1807588210772148225");
        // 当前月份
        int currentMonth = LocalDateTime.now().getMonthValue();
        list = list.stream().filter(e -> e.getMonth() == null || e.getMonth() == currentMonth).toList();
        // sap客户名称
        Set<String> customerNames = new HashSet<>();
        // 人员
        Set<String> userNames = new HashSet<>();
        // 合同
        Set<String> importContractCode = new HashSet<>();
        Set<String> customerCodes = new HashSet<>();
        for (HistoricalFinancialDataImportModel model : list) {
            customerNames.add(model.getCustomerName());
            customerNames.add(model.getCustomerName2());
            customerCodes.add(model.getCustomerCode());
            userNames.add(model.getSalesMan());
            userNames.add(model.getSalesMan2());
            importContractCode.add(model.getContractCode());
            importContractCode.add(model.getContractCode2());
        }
        LambdaQueryWrapper<SapCustomer> sapCustomerQueryWrapper = new LambdaQueryWrapper<>();
        ThreadLocalHolder.setIgnoreInterceptor();
        sapCustomerQueryWrapper.select(SapCustomer::getId, SapCustomer::getSapNo, SapCustomer::getName, SapCustomer::getTenantId).in(SapCustomer::getName, customerNames).or().in(SapCustomer::getSapNo, customerCodes);
        List<SapCustomer> sapCustomers = sapCustomerMapper.selectList(sapCustomerQueryWrapper);
        Map<String, String> sapCustomerMap = new HashMap<>();
        Map<String, String> sapCustomerNameMap = new HashMap<>();
        for (SapCustomer sapCustomer : sapCustomers) {
            sapCustomerMap.put(sapCustomer.getTenantId() + sapCustomer.getSapNo(), sapCustomer.getId());
            sapCustomerNameMap.put(sapCustomer.getTenantId() + sapCustomer.getName(), sapCustomer.getId());
        }
        List<IamUser> users = iamUserMapper.selectList(new LambdaQueryWrapper<IamUser>().select(IamUser::getId, IamUser::getRealname).in(IamUser::getRealname, userNames));
        Map<String, String> userMap = users.stream().collect(Collectors.toMap(IamUser::getRealname, IamUser::getId));
        QueryWrapper<Contract> contractQueryWrapper = new QueryWrapper<>();
        contractQueryWrapper.lambda().select(Contract::getContractCode).in(Contract::getContractCode, importContractCode);
        List<Contract> contracts = contractMapper.selectList(contractQueryWrapper);
        if (contracts != null && !contracts.isEmpty()) {
            Set<String> existCodes = contracts.stream().map(Contract::getContractCode).collect(Collectors.toSet());
            list = list.stream().filter(historicalFinancialDataImportModel -> !existCodes.contains(historicalFinancialDataImportModel.getContractCode())).toList();
        }
        List<ProjectContractAuditDetailVO> contractDetail = new ArrayList<>();
        for (HistoricalFinancialDataImportModel historicalFinancialDataImportModel : list) {
            String tenant = tenantMap.get(historicalFinancialDataImportModel.getTenant()) == null ? tenantMap.get(historicalFinancialDataImportModel.getTenant2()) : tenantMap.get(historicalFinancialDataImportModel.getTenant());
            if (tenant == null) {
                continue;
            }
            String contractCode = historicalFinancialDataImportModel.getContractCode() == null ? historicalFinancialDataImportModel.getContractCode2() : historicalFinancialDataImportModel.getContractCode();
            String customerName = historicalFinancialDataImportModel.getCustomerName() == null ? historicalFinancialDataImportModel.getCustomerName2() : historicalFinancialDataImportModel.getCustomerName();
            String saler = historicalFinancialDataImportModel.getSalesMan() == null ? historicalFinancialDataImportModel.getSalesMan2() : historicalFinancialDataImportModel.getSalesMan();
            BigDecimal totalAmount = historicalFinancialDataImportModel.getContractSalesTotalAmount() == null ? historicalFinancialDataImportModel.getContractSalesTotalAmount2() : historicalFinancialDataImportModel.getContractSalesTotalAmount();

            ProjectContractAuditDetailVO detailVO = new ProjectContractAuditDetailVO();
            detailVO.setContractCode(contractCode)
                    .setSapContractCode(historicalFinancialDataImportModel.getSapContractCode())
                    .setCustomerName(customerName)
                    .setWriteDate(historicalFinancialDataImportModel.getWriteDate() == null ? LocalDateTime.now() : historicalFinancialDataImportModel.getWriteDate().atTime(LocalTime.MIN))
                    .setAging(historicalFinancialDataImportModel.getAging())
                    .setContractModel(historicalFinancialDataImportModel.getContractType())
                    .setSaler(userMap.get(saler))
                    .setCate(2) // 老应收
                    .setContractStatus(ContractAuditStatusEnum.DONE.getCode()) // 已完成
                    .setTenantId(tenant);
            detailVO.setCustomerId(getSapCustomerId(sapCustomerMap, sapCustomerNameMap, historicalFinancialDataImportModel.getCustomerCode(), detailVO.getCustomerName(), detailVO.getTenantId()));
            ProjectContractAmountDetailVO amount = new ProjectContractAmountDetailVO();
            amount.setContractSalesTotalAmount(totalAmount == null ? BigDecimal.ZERO : totalAmount)
                    .setCollectedAmount(historicalFinancialDataImportModel.getCollectedAmount() == null ? BigDecimal.ZERO : historicalFinancialDataImportModel.getCollectedAmount())
                    .setUncollectedAmount(amount.getContractSalesTotalAmount().subtract(amount.getCollectedAmount()))
                    .setTenantId(tenant);
            detailVO.setProjectContractAmount(amount);
            contractDetail.add(detailVO);
        }
        this.createEntities(contractDetail);
        List<ProjectContractAmount> projectContractAmounts = new ArrayList<>();
        for (ProjectContractAuditDetailVO detailVO : contractDetail) {
            ProjectContractAmountDetailVO projectContractAmount = detailVO.getProjectContractAmount();
            projectContractAmount.setContractId(detailVO.getId());
            projectContractAmounts.add(projectContractAmount);
        }
        projectContractAmountService.createEntities(projectContractAmounts);
    }

    /**
     * 获取sap客户id
     *
     * @param sapCustomerMap
     * @param sapCustomerNameMap
     * @param customerCode
     * @param customerName
     * @param tenantId
     * @return
     * @author 陈广煜
     * @date 2025-03-15
     */
    private String getSapCustomerId(Map<String, String> sapCustomerMap, Map<String, String> sapCustomerNameMap, String customerCode, String customerName, String tenantId) {
        // 数控、国际、专项共用sap客户
        List<IamTenant> list = iamTenantService.lambdaQuery().select(IamTenant::getId, IamTenant::getOaNum).list();
        Map<String, List<IamTenant>> groupByOaNum = list.stream().filter(e -> e.getOaNum() != null).collect(Collectors.groupingBy(IamTenant::getOaNum));
        for (Map.Entry<String, List<IamTenant>> ent : groupByOaNum.entrySet()) {
            List<IamTenant> tenants = ent.getValue();
            List<String> tenantIds = tenants.stream().map(IamTenant::getId).toList();
            if (tenantIds.contains(tenantId)) {
                for (String id : tenantIds) {
                    String s = sapCustomerMap.get(id + customerCode) != null ? sapCustomerMap.get(id + customerCode) : sapCustomerNameMap.get(id + customerName);
                    if (s != null) {
                        return s;
                    }
                }
            }
        }
        return sapCustomerMap.get(tenantId + customerCode) != null ? sapCustomerMap.get(tenantId + customerCode) : sapCustomerNameMap.get(tenantId + customerName);
    }
}